Pythonã¬ãŒãå¶éæè¡ãæ¢æ±ããAPIä¿è·ãšãã©ãã£ãã¯ç®¡çã®ããã®ããŒã¯ã³ãã±ãããšã¹ã©ã€ãã£ã³ã°ãŠã£ã³ããŠã¢ã«ãŽãªãºã ãæ¯èŒããŸãã
Pythonã¬ãŒãå¶éïŒããŒã¯ã³ãã±ãã vs. ã¹ã©ã€ãã£ã³ã°ãŠã£ã³ã㊠- å æ¬çãªã¬ã€ã
仿¥ã®çžäºæ¥ç¶ãããäžçã§ã¯ãå ç¢ãªAPIã¯ã¢ããªã±ãŒã·ã§ã³ã®æåã«äžå¯æ¬ ã§ããããããAPIãžã®å¶åŸ¡ãããŠããªãã¢ã¯ã»ã¹ã¯ããµãŒããŒã®éè² è·ããµãŒãã¹ã®å£åãããã«ã¯ãµãŒãã¹æåŠïŒDoSïŒæ»æã«ã€ãªããå¯èœæ§ããããŸããã¬ãŒãå¶éã¯ããŠãŒã¶ãŒãŸãã¯ãµãŒãã¹ãç¹å®ã®æéæ å ã§å®è¡ã§ãããªã¯ãšã¹ãã®æ°ãå¶éããããšã«ãããAPIãä¿è·ããããã®éèŠãªæè¡ã§ãããã®èšäºã§ã¯ãPythonã§äººæ°ã®ãã2ã€ã®ã¬ãŒãå¶éã¢ã«ãŽãªãºã ãããŒã¯ã³ãã±ãããšã¹ã©ã€ãã£ã³ã°ãŠã£ã³ããŠã«ã€ããŠè©³ãã説æããå æ¬çãªæ¯èŒãšå®è·µçãªå®è£ äŸãæäŸããŸãã
ãªãã¬ãŒãå¶éãéèŠãªã®ã
ã¬ãŒãå¶éã«ã¯ã次ã®ãããªå€ãã®å©ç¹ããããŸãã
- äžæ£è¡çºã®é²æ¢ïŒæªæã®ãããŠãŒã¶ãŒãããããéå°ãªãªã¯ãšã¹ãã§ãµãŒããŒãå§åããã®ãå¶éããŸãã
- å ¬æ£ãªäœ¿çšã®ç¢ºä¿ïŒãŠãŒã¶ãŒéã§ãªãœãŒã¹ãå ¬å¹³ã«åé ãã1人ã®ãŠãŒã¶ãŒãã·ã¹ãã ãç¬å ããã®ãé²ããŸãã
- ã€ã³ãã©ã¹ãã©ã¯ãã£ã®ä¿è·ïŒãµãŒããŒãšããŒã¿ããŒã¹ãéè² è·ã«ãªããã¯ã©ãã·ã¥ããã®ãé²ããŸãã
- ã³ã¹ãã®ç®¡çïŒäºæããªããªãœãŒã¹æ¶è²»ã®æ¥å¢ãé²ããã³ã¹ãåæžã«ã€ãªãããŸãã
- ããã©ãŒãã³ã¹ã®åäžïŒãªãœãŒã¹ã®æ¯æžãé²ããäžè²«ããå¿çæéã確ä¿ããããšã«ãããå®å®ããããã©ãŒãã³ã¹ãç¶æããŸãã
ã¬ãŒãå¶éã¢ã«ãŽãªãºã ã®çè§£
ããã€ãã®ã¬ãŒãå¶éã¢ã«ãŽãªãºã ãååšããããããã«ç¬èªã®é·æãšçæããããŸããæãäžè¬çã«äœ¿çšãããŠãã2ã€ã®ã¢ã«ãŽãªãºã ãããŒã¯ã³ãã±ãããšã¹ã©ã€ãã£ã³ã°ãŠã£ã³ããŠã«çŠç¹ãåœãŠãŸãã
1. ããŒã¯ã³ãã±ããã¢ã«ãŽãªãºã
ããŒã¯ã³ãã±ããã¢ã«ãŽãªãºã ã¯ãã·ã³ãã«ã§åºã䜿çšãããŠããã¬ãŒãå¶éæè¡ã§ããããŒã¯ã³ãä¿æããããã±ããããç¶æããããšã«ãã£ãŠæ©èœããŸããåããŒã¯ã³ã¯ã1ã€ã®ãªã¯ãšã¹ããè¡ãèš±å¯ã衚ããŸãããã±ããã«ã¯æå€§å®¹éããããããŒã¯ã³ã¯åºå®ã¬ãŒãã§ãã±ããã«è¿œå ãããŸãã
ãªã¯ãšã¹ããå°çãããšãã¬ãŒããªããã¿ã¯ãã±ããã«ååãªããŒã¯ã³ããããã©ããã確èªããŸããååãªããŒã¯ã³ãããå Žåã¯ããªã¯ãšã¹ããèš±å¯ããã察å¿ããæ°ã®ããŒã¯ã³ããã±ããããåé€ãããŸãããã±ããã空ã®å Žåããªã¯ãšã¹ãã¯æåŠãããããååãªããŒã¯ã³ãå©çšå¯èœã«ãªããŸã§é å»¶ããŸãã
Pythonã§ã®ããŒã¯ã³ãã±ããã®å®è£
以äžã¯ãåæå®è¡ã管çããããã«threadingã¢ãžã¥ãŒã«ã䜿çšãããããŒã¯ã³ãã±ããã¢ã«ãŽãªãºã ã®åºæ¬çãªPythonå®è£
ã§ãã
import time
import threading
class TokenBucket:
def __init__(self, capacity, fill_rate):
self.capacity = float(capacity)
self._tokens = float(capacity)
self.fill_rate = float(fill_rate)
self.last_refill = time.monotonic()
self.lock = threading.Lock()
def _refill(self):
now = time.monotonic()
delta = now - self.last_refill
tokens_to_add = delta * self.fill_rate
self._tokens = min(self.capacity, self._tokens + tokens_to_add)
self.last_refill = now
def consume(self, tokens):
with self.lock:
self._refill()
if self._tokens >= tokens:
self._tokens -= tokens
return True
return False
# Example Usage
bucket = TokenBucket(capacity=10, fill_rate=2) # 10 tokens, refill at 2 tokens per second
for i in range(15):
if bucket.consume(1):
print(f"Request {i+1}: Allowed")
else:
print(f"Request {i+1}: Rate Limited")
time.sleep(0.2)
説æïŒ
TokenBucket(capacity, fill_rate)ïŒæå€§å®¹éãšå å¡«çïŒ1ç§ãããã®ããŒã¯ã³æ°ïŒã§ãã±ãããåæåããŸãã_refill()ïŒæåŸã®å å¡«ããã®çµéæéã«åºã¥ããŠãããŒã¯ã³ã§ãã±ãããå å¡«ããŸããconsume(tokens)ïŒæå®ãããæ°ã®ããŒã¯ã³ãæ¶è²»ããããšããŸããæåããå ŽåïŒãªã¯ãšã¹ããèš±å¯ãããå ŽåïŒã¯Trueããã以å€ã®å ŽåïŒãªã¯ãšã¹ããã¬ãŒãå¶éãããå ŽåïŒã¯Falseãè¿ããŸãã- ã¹ã¬ããããã¯ïŒåæå®è¡ç°å¢ã§ã®ã¹ã¬ããã®å®å
šæ§ã確ä¿ããããã«ãã¹ã¬ããããã¯ïŒ
self.lockïŒã䜿çšããŸãã
ããŒã¯ã³ãã±ããã®å©ç¹
- å®è£ ãç°¡åïŒçè§£ããå®è£ ããã®ãæ¯èŒçç°¡åã§ãã
- ããŒã¹ãåŠçïŒãã±ããã«ååãªããŒã¯ã³ãããéããææã®ãã©ãã£ãã¯ã®ããŒã¹ããåŠçã§ããŸãã
- æ§æå¯èœïŒå®¹éãšå å¡«çã¯ãç¹å®ã®èŠä»¶ã«åãããŠç°¡åã«èª¿æŽã§ããŸãã
ããŒã¯ã³ãã±ããã®æ¬ ç¹
- å®å šã«æ£ç¢ºã§ã¯ãªãïŒå å¡«ã¡ã«ããºã ã«ãããæ§æãããã¬ãŒãããããããã«å€ãã®ãªã¯ãšã¹ããèš±å¯ããå ŽåããããŸãã
- ãã©ã¡ãŒã¿ã®èª¿æŽïŒç®çã®ã¬ãŒãå¶éåäœãå®çŸããã«ã¯ã容éãšå å¡«çãæ éã«éžæããå¿ èŠããããŸãã
2. ã¹ã©ã€ãã£ã³ã°ãŠã£ã³ããŠã¢ã«ãŽãªãºã
ã¹ã©ã€ãã£ã³ã°ãŠã£ã³ããŠã¢ã«ãŽãªãºã ã¯ãæéãåºå®ãµã€ãºã®ãŠã£ã³ããŠã«åå²ãããããæ£ç¢ºãªã¬ãŒãå¶éæè¡ã§ããåãŠã£ã³ããŠå ã§è¡ããããªã¯ãšã¹ãã®æ°ã远跡ããŸããæ°ãããªã¯ãšã¹ããå°çãããšãã¢ã«ãŽãªãºã ã¯çŸåšã®ãŠã£ã³ããŠå ã®ãªã¯ãšã¹ãã®æ°ãå¶éãè¶ ããŠãããã©ããã確èªããŸããè¶ ããŠããå Žåããªã¯ãšã¹ãã¯æåŠãããããé å»¶ããŸãã
ãã¹ã©ã€ãã£ã³ã°ããšããåŽé¢ã¯ãæ°ãããªã¯ãšã¹ããå°çãããšããŠã£ã³ããŠãæéå ã«åé²ããããšããæ¥ãŠããŸããçŸåšã®ãŠã£ã³ããŠãçµäºãããšãæ°ãããŠã£ã³ããŠãéå§ãããã«ãŠã³ãããªã»ãããããŸããã¹ã©ã€ãã£ã³ã°ãŠã£ã³ããŠã¢ã«ãŽãªãºã ã«ã¯ãã¹ã©ã€ãã£ã³ã°ãã°ãšåºå®ãŠã£ã³ããŠã«ãŠã³ã¿ãŒã®2ã€ã®äž»èŠãªããªãšãŒã·ã§ã³ããããŸãã
2.1. ã¹ã©ã€ãã£ã³ã°ãã°
ã¹ã©ã€ãã£ã³ã°ãã°ã¢ã«ãŽãªãºã ã¯ãç¹å®ã®æéãŠã£ã³ããŠå ã«è¡ããããã¹ãŠã®ãªã¯ãšã¹ãã®ã¿ã€ã ã¹ã¿ã³ãä»ããã°ãç¶æããŸããæ°ãããªã¯ãšã¹ããæ¥ããšãããŠã£ã³ããŠå ã«å ¥ããã°å ã®ãã¹ãŠã®ãªã¯ãšã¹ããåèšãããããã¬ãŒãå¶éãšæ¯èŒããŸããããã¯æ£ç¢ºã§ãããã¡ã¢ãªãšåŠçèœåã®ç¹ã§ã³ã¹ãããããå¯èœæ§ããããŸãã
2.2. åºå®ãŠã£ã³ããŠã«ãŠã³ã¿ãŒ
åºå®ãŠã£ã³ããŠã«ãŠã³ã¿ãŒã¢ã«ãŽãªãºã ã¯ãæéãåºå®ãŠã£ã³ããŠã«åå²ããåãŠã£ã³ããŠã®ã«ãŠã³ã¿ãŒãä¿æããŸããæ°ãããªã¯ãšã¹ããå°çãããšãã¢ã«ãŽãªãºã ã¯çŸåšã®ãŠã£ã³ããŠã®ã«ãŠã³ã¿ãŒãã€ã³ã¯ãªã¡ã³ãããŸããã«ãŠã³ã¿ãŒãå¶éãè¶ ãããšããªã¯ãšã¹ãã¯æåŠãããŸããããã¯ã¹ã©ã€ãã£ã³ã°ãã°ãããç°¡åã§ããã2ã€ã®ãŠã£ã³ããŠã®å¢çã§ããŒã¹ãã®ãªã¯ãšã¹ããèš±å¯ããå¯èœæ§ããããŸãã
Pythonã§ã®ã¹ã©ã€ãã£ã³ã°ãŠã£ã³ããŠã®å®è£ ïŒåºå®ãŠã£ã³ããŠã«ãŠã³ã¿ãŒïŒ
以äžã¯ãåºå®ãŠã£ã³ããŠã«ãŠã³ã¿ãŒã¢ãããŒãã䜿çšãããã¹ã©ã€ãã£ã³ã°ãŠã£ã³ããŠã¢ã«ãŽãªãºã ã®Pythonå®è£ ã§ãã
import time
import threading
class SlidingWindowCounter:
def __init__(self, window_size, max_requests):
self.window_size = window_size # seconds
self.max_requests = max_requests
self.request_counts = {}
self.lock = threading.Lock()
def is_allowed(self, client_id):
with self.lock:
current_time = int(time.time())
window_start = current_time - self.window_size
# Clean up old requests
self.request_counts = {ts: count for ts, count in self.request_counts.items() if ts > window_start}
total_requests = sum(self.request_counts.values())
if total_requests < self.max_requests:
self.request_counts[current_time] = self.request_counts.get(current_time, 0) + 1
return True
else:
return False
# Example Usage
window_size = 60 # 60 seconds
max_requests = 10 # 10 requests per minute
rate_limiter = SlidingWindowCounter(window_size, max_requests)
client_id = "user123"
for i in range(15):
if rate_limiter.is_allowed(client_id):
print(f"Request {i+1}: Allowed")
else:
print(f"Request {i+1}: Rate Limited")
time.sleep(5)
説æïŒ
SlidingWindowCounter(window_size, max_requests)ïŒãŠã£ã³ããŠãµã€ãºïŒç§åäœïŒãšããŠã£ã³ããŠå ã§èš±å¯ããããªã¯ãšã¹ãã®æå€§æ°ãåæåããŸããis_allowed(client_id)ïŒã¯ã©ã€ã¢ã³ãããªã¯ãšã¹ããéä¿¡ã§ãããã©ããã確èªããŸãããŠã£ã³ããŠå€ã®å€ããªã¯ãšã¹ããã¯ãªãŒã³ã¢ããããæ®ãã®ãªã¯ãšã¹ããåèšããå¶éãè¶ ããŠããªãå Žåã¯çŸåšã®ãŠã£ã³ããŠã®ã«ãŠã³ããã€ã³ã¯ãªã¡ã³ãããŸããself.request_countsïŒãªã¯ãšã¹ãã®ã¿ã€ã ã¹ã¿ã³ããšãã®ã«ãŠã³ããæ ŒçŽããèŸæžã§ãå€ããªã¯ãšã¹ãã®éèšãšã¯ãªãŒã³ã¢ãããå¯èœã«ããŸã- ã¹ã¬ããããã¯ïŒåæå®è¡ç°å¢ã§ã®ã¹ã¬ããã®å®å
šæ§ã確ä¿ããããã«ãã¹ã¬ããããã¯ïŒ
self.lockïŒã䜿çšããŸãã
ã¹ã©ã€ãã£ã³ã°ãŠã£ã³ããŠã®å©ç¹
- ããæ£ç¢ºïŒç¹ã«ã¹ã©ã€ãã£ã³ã°ãã°ã®å®è£ ã§ã¯ãããŒã¯ã³ãã±ãããããæ£ç¢ºãªã¬ãŒãå¶éãæäŸããŸãã
- å¢çããŒã¹ãã®é²æ¢ïŒ2ã€ã®æéãŠã£ã³ããŠã®å¢çã§ã®ããŒã¹ãã®å¯èœæ§ãæžãããŸãïŒã¹ã©ã€ãã£ã³ã°ãã°ã§ãã广çïŒã
ã¹ã©ã€ãã£ã³ã°ãŠã£ã³ããŠã®æ¬ ç¹
- ããè€éïŒããŒã¯ã³ãã±ãããšæ¯èŒããŠãå®è£ ãšçè§£ãããè€éã§ãã
- ããé«ããªãŒããŒãããïŒç¹ã«ã¹ã©ã€ãã£ã³ã°ãã°ã®å®è£ ã§ã¯ããªã¯ãšã¹ããã°ãä¿åããŠåŠçããå¿ èŠããããããããé«ããªãŒããŒããããçºçããå¯èœæ§ããããŸãã
ããŒã¯ã³ãã±ãã vs. ã¹ã©ã€ãã£ã³ã°ãŠã£ã³ããŠïŒè©³çŽ°ãªæ¯èŒ
ããŒã¯ã³ãã±ããã¢ã«ãŽãªãºã ãšã¹ã©ã€ãã£ã³ã°ãŠã£ã³ããŠã¢ã«ãŽãªãºã ã®äž»ãªéãããŸãšããè¡šãæ¬¡ã«ç€ºããŸãã
| æ©èœ | ããŒã¯ã³ãã±ãã | ã¹ã©ã€ãã£ã³ã°ãŠã£ã³ã㊠|
|---|---|---|
| è€éã | ããã·ã³ãã« | ããè€é |
| 粟床 | 粟床ãäœã | ããæ£ç¢º |
| ããŒã¹ãåŠç | è¯å¥œ | è¯å¥œïŒç¹ã«ã¹ã©ã€ãã£ã³ã°ãã°ïŒ |
| ãªãŒããŒããã | äœã | é«ãïŒç¹ã«ã¹ã©ã€ãã£ã³ã°ãã°ïŒ |
| å®è£ åªå | ç°¡å | é£ãã |
é©åãªã¢ã«ãŽãªãºã ã®éžæ
ããŒã¯ã³ãã±ãããšã¹ã©ã€ãã£ã³ã°ãŠã£ã³ããŠã®éžæã¯ãç¹å®ã®èŠä»¶ãšåªå é äœã«ãã£ãŠç°ãªããŸããæ¬¡ã®èŠçŽ ãèæ ®ããŠãã ããã
- 粟床ïŒéåžžã«æ£ç¢ºãªã¬ãŒãå¶éãå¿ èŠãªå Žåã¯ãã¹ã©ã€ãã£ã³ã°ãŠã£ã³ããŠã¢ã«ãŽãªãºã ãäžè¬çã«æšå¥šãããŸãã
- è€éãïŒã·ã³ãã«ããåªå ãããå Žåã¯ãããŒã¯ã³ãã±ããã¢ã«ãŽãªãºã ãé©ããŠããŸãã
- ããã©ãŒãã³ã¹ïŒããã©ãŒãã³ã¹ãéèŠã§ããå Žåã¯ãã¹ã©ã€ãã£ã³ã°ãŠã£ã³ããŠã¢ã«ãŽãªãºã ïŒç¹ã«ã¹ã©ã€ãã£ã³ã°ãã°ã®å®è£ ïŒã®ãªãŒããŒããããæ éã«æ€èšããŠãã ããã
- ããŒã¹ãåŠçïŒã©ã¡ãã®ã¢ã«ãŽãªãºã ããã©ãã£ãã¯ã®ããŒã¹ããåŠçã§ããŸãããã¹ã©ã€ãã£ã³ã°ãŠã£ã³ããŠïŒã¹ã©ã€ãã£ã³ã°ãã°ïŒã¯ãããŒã¹ãç¶æ ã§ã®ããäžè²«ããã¬ãŒãå¶éãæäŸããŸãã
- ã¹ã±ãŒã©ããªãã£ïŒé«åºŠã«ã¹ã±ãŒã©ãã«ãªã·ã¹ãã ã®å Žåã¯ãïŒä»¥äžã§èª¬æããïŒåæ£ã¬ãŒãå¶éæè¡ã®äœ¿çšãæ€èšããŠãã ããã
å€ãã®å ŽåãããŒã¯ã³ãã±ããã¢ã«ãŽãªãºã ã¯ãæ¯èŒçäœãå®è£ ã³ã¹ãã§ååãªã¬ãã«ã®ã¬ãŒãå¶éãæäŸããŸãããã ããããæ£ç¢ºãªã¬ãŒãå¶éãå¿ èŠã§ãè€éãã®å¢å ã«èããããã¢ããªã±ãŒã·ã§ã³ã®å Žåã¯ãã¹ã©ã€ãã£ã³ã°ãŠã£ã³ããŠã¢ã«ãŽãªãºã ã®æ¹ãé©ããŠããŸãã
忣ã¬ãŒãå¶é
è€æ°ã®ãµãŒããŒããªã¯ãšã¹ããåŠçãã忣ã·ã¹ãã ã§ã¯ããã¹ãŠã®ãµãŒããŒã§äžè²«ããã¬ãŒãå¶éã確ä¿ããããã«ãéäžåã®ã¬ãŒãå¶éã¡ã«ããºã ãå¿ èŠã«ãªãããšããããããŸãã忣ã¬ãŒãå¶éã«ã¯ãããã€ãã®æ¹æ³ã䜿çšã§ããŸãã
- éäžåããŒã¿ã¹ãã¢ïŒRedisãMemcachedãªã©ã®éäžåããŒã¿ã¹ãã¢ã䜿çšããŠãã¬ãŒãå¶éç¶æ ïŒããšãã°ãããŒã¯ã³ã«ãŠã³ããŸãã¯ãªã¯ãšã¹ããã°ïŒãä¿åããŸãããã¹ãŠã®ãµãŒããŒã¯ãå ±æããŒã¿ã¹ãã¢ã«ã¢ã¯ã»ã¹ããŠæŽæ°ããã¬ãŒãå¶éãé©çšããŸãã
- ããŒããã©ã³ãµãŒã®ã¬ãŒãå¶éïŒããŒããã©ã³ãµãŒãæ§æããŠãIPã¢ãã¬ã¹ããŠãŒã¶ãŒIDããŸãã¯ãã®ä»ã®åºæºã«åºã¥ããŠã¬ãŒãå¶éãå®è¡ããŸãããã®ã¢ãããŒãã«ãããã¢ããªã±ãŒã·ã§ã³ãµãŒããŒããã¬ãŒãå¶éããªãããŒãã§ããŸãã
- å°çšã®ã¬ãŒãå¶éãµãŒãã¹ïŒãã¹ãŠã®ã¬ãŒãå¶éãªã¯ãšã¹ããåŠçããå°çšã®ã¬ãŒãå¶éãµãŒãã¹ãäœæããŸãããã®ãµãŒãã¹ã¯ãããã©ãŒãã³ã¹ã®ããã«åå¥ã«ã¹ã±ãŒãªã³ã°ããã³æé©åã§ããŸãã
- ã¯ã©ã€ã¢ã³ãåŽã®ã¬ãŒãå¶éïŒäž»èŠãªé²åŸ¡ã§ã¯ãããŸããããHTTPããããŒïŒäŸïŒ
X-RateLimit-LimitãX-RateLimit-RemainingãX-RateLimit-ResetïŒãä»ããŠã¯ã©ã€ã¢ã³ãã«ã¬ãŒãå¶éãéç¥ããŸããããã«ãããã¯ã©ã€ã¢ã³ããèªå·±èª¿æŽãè¡ããäžèŠãªãªã¯ãšã¹ããæžããããšãã§ããŸãã
忣ã¬ãŒãå¶éã«ããŒã¯ã³ãã±ããã¢ã«ãŽãªãºã ãšRedisã䜿çšããäŸã次ã«ç€ºããŸãã
import redis
import time
class RedisTokenBucket:
def __init__(self, redis_client, bucket_key, capacity, fill_rate):
self.redis_client = redis_client
self.bucket_key = bucket_key
self.capacity = capacity
self.fill_rate = fill_rate
def consume(self, tokens):
now = time.time()
capacity = self.capacity
fill_rate = self.fill_rate
# Lua script to atomically update the token bucket in Redis
script = '''
local bucket_key = KEYS[1]
local capacity = tonumber(ARGV[1])
local fill_rate = tonumber(ARGV[2])
local tokens_to_consume = tonumber(ARGV[3])
local now = tonumber(ARGV[4])
local last_refill = redis.call('get', bucket_key .. ':last_refill')
if not last_refill then
last_refill = now
redis.call('set', bucket_key .. ':last_refill', now)
else
last_refill = tonumber(last_refill)
end
local tokens = redis.call('get', bucket_key .. ':tokens')
if not tokens then
tokens = capacity
redis.call('set', bucket_key .. ':tokens', capacity)
else
tokens = tonumber(tokens)
end
-- Refill the bucket
local time_since_last_refill = now - last_refill
local tokens_to_add = time_since_last_refill * fill_rate
tokens = math.min(capacity, tokens + tokens_to_add)
-- Consume tokens
if tokens >= tokens_to_consume then
tokens = tokens - tokens_to_consume
redis.call('set', bucket_key .. ':tokens', tokens)
redis.call('set', bucket_key .. ':last_refill', now)
return 1 -- Success
else
return 0 -- Rate limited
end
'''
# Execute the Lua script
consume_script = self.redis_client.register_script(script)
result = consume_script(keys=[self.bucket_key], args=[capacity, fill_rate, tokens, now])
return result == 1
# Example Usage
redis_client = redis.StrictRedis(host='localhost', port=6379, db=0)
bucket = RedisTokenBucket(redis_client, bucket_key='my_api:user123', capacity=10, fill_rate=2)
for i in range(15):
if bucket.consume(1):
print(f"Request {i+1}: Allowed")
else:
print(f"Request {i+1}: Rate Limited")
time.sleep(0.2)
忣ã·ã¹ãã ã®éèŠãªèæ ®äºé ïŒ
- ååæ§ïŒããŒã¯ã³ã®æ¶è²»ãŸãã¯ãªã¯ãšã¹ãã®ã«ãŠã³ãæäœãã¢ãããã¯ã§ããããšã確èªããŠãç«¶åç¶æ ãåé¿ããŸããRedis Luaã¹ã¯ãªããã¯ãã¢ãããã¯ãªæäœãæäŸããŸãã
- åŸ ã¡æéïŒéäžåããŒã¿ã¹ãã¢ã«ã¢ã¯ã»ã¹ãããšãã®ãããã¯ãŒã¯åŸ ã¡æéãæå°éã«æããŸãã
- ã¹ã±ãŒã©ããªãã£ïŒäºæ³ãããè² è·ãåŠçã§ããããŒã¿ã¹ãã¢ãéžæããŸãã
- ããŒã¿ã®æŽåæ§ïŒåæ£ç°å¢ã«ãããæœåšçãªããŒã¿ã®æŽåæ§ã®åé¡ã«å¯ŸåŠããŸãã
ã¬ãŒãå¶éã®ãã¹ããã©ã¯ãã£ã¹
ã¬ãŒãå¶éãå®è£ ããéã«åŸãã¹ããã¹ããã©ã¯ãã£ã¹ã次ã«ç€ºããŸãã
- ã¬ãŒãå¶éèŠä»¶ã®ç¹å®ïŒAPIãšã³ããã€ã³ããšãŠãŒã¶ãŒã°ã«ãŒãããšã«ã䜿çšãã¿ãŒã³ãšãªãœãŒã¹æ¶è²»éã«åºã¥ããŠé©åãªã¬ãŒãå¶éãæ±ºå®ããŸãããµãã¹ã¯ãªãã·ã§ã³ã¬ãã«ã«åºã¥ããŠéå±€ã¢ã¯ã»ã¹ãæäŸããããšãæ€èšããŠãã ããã
- æå³ã®ããHTTPã¹ããŒã¿ã¹ã³ãŒãã䜿çšããïŒ
429 Too Many Requestsãªã©ãã¬ãŒãå¶éã瀺ãé©åãªHTTPã¹ããŒã¿ã¹ã³ãŒããè¿ããŸãã - ã¬ãŒãå¶éããããŒãå«ããïŒAPIå¿çã«ã¬ãŒãå¶éããããŒãå«ããŠãã¯ã©ã€ã¢ã³ãã«çŸåšã®ã¬ãŒãå¶éã¹ããŒã¿ã¹ïŒäŸïŒ
X-RateLimit-LimitãX-RateLimit-RemainingãX-RateLimit-ResetïŒãéç¥ããŸãã - æç¢ºãªãšã©ãŒã¡ãã»ãŒãžãæäŸããïŒã¬ãŒãå¶éããããšãã«ãçç±ã説æããåé¡ã®è§£æ±ºæ¹æ³ãææ¡ããæçãªãšã©ãŒã¡ãã»ãŒãžãã¯ã©ã€ã¢ã³ãã«æäŸããŸãããµããŒãçšã®é£çµ¡å æ å ±ãæäŸããŸãã
- ã°ã¬ãŒã¹ãã«ãã°ã©ããŒã·ã§ã³ã®å®è£ ïŒã¬ãŒãå¶éãé©çšãããŠããå Žåã¯ããªã¯ãšã¹ããå®å šã«ãããã¯ããã®ã§ã¯ãªããäœäžãããµãŒãã¹ãæäŸããããšãæ€èšããŠãã ãããããšãã°ããã£ãã·ã¥ãããããŒã¿ãŸãã¯æ©èœã®åæžãæäŸããŸãã
- ã¬ãŒãå¶éã®ç£èŠãšåæïŒã¬ãŒãå¶éã·ã¹ãã ãç£èŠããŠãæœåšçãªåé¡ãç¹å®ãããã®ããã©ãŒãã³ã¹ãæé©åããŸããå¿ èŠã«å¿ããŠã¬ãŒãå¶éã調æŽããããã«ã䜿çšãã¿ãŒã³ãåæããŸãã
- ã¬ãŒãå¶éã®ä¿è·ïŒãªã¯ãšã¹ããæ€èšŒããé©åãªã»ãã¥ãªãã£å¯Ÿçãå®è£ ããããšã«ããããŠãŒã¶ãŒãã¬ãŒãå¶éããã€ãã¹ããã®ãé²ããŸãã
- ã¬ãŒãå¶éã®ããã¥ã¡ã³ãåïŒAPIããã¥ã¡ã³ãã§ã¬ãŒãå¶éããªã·ãŒãæç¢ºã«ããã¥ã¡ã³ãåããŸããã¯ã©ã€ã¢ã³ããã¬ãŒãå¶éãåŠçããæ¹æ³ã瀺ãã³ãŒãäŸãæäŸããŸãã
- å®è£ ããã¹ãããïŒããŸããŸãªè² è·æ¡ä»¶äžã§ã¬ãŒãå¶éã®å®è£ ã培åºçã«ãã¹ãããŠãæ£ããæ©èœããŠããããšã確èªããŸãã
- å°åå·®ãèæ ®ããïŒã°ããŒãã«ã«å±éããå Žåã¯ããããã¯ãŒã¯åŸ ã¡æéãšãŠãŒã¶ãŒã®è¡åã«ãããå°åå·®ãèæ ®ããŠãã ãããå°åããšã«ã¬ãŒãå¶éã調æŽããå¿ èŠãããå ŽåããããŸããããšãã°ãã€ã³ãã®ãããªã¢ãã€ã«ãã¡ãŒã¹ãåžå Žã§ã¯ãéåœã®ãããªé«åž¯åå¹ å°åãšæ¯èŒããŠãç°ãªãã¬ãŒãå¶éãå¿ èŠã«ãªãå ŽåããããŸãã
å®éã®äŸ
- TwitterïŒTwitterã¯ãäžæ£äœ¿çšããAPIãä¿è·ããå ¬æ£ãªäœ¿çšã確ä¿ããããã«ã¬ãŒãå¶éãåºç¯å²ã«äœ¿çšããŠããŸããã¬ãŒãå¶éã«é¢ãã詳现ãªããã¥ã¡ã³ããæäŸããHTTPããããŒã䜿çšããŠãéçºè ã«ã¬ãŒãå¶éã¹ããŒã¿ã¹ãéç¥ããŸãã
- GitHubïŒGitHubããäžæ£äœ¿çšãé²ããAPIã®å®å®æ§ãç¶æããããã«ã¬ãŒãå¶éã䜿çšããŠããŸããIPããŒã¹ãšãŠãŒã¶ãŒããŒã¹ã®ã¬ãŒãå¶éãçµã¿åãããŠäœ¿çšââããŠããŸãã
- StripeïŒStripeã¯ãæ¯æãåŠçAPIãäžæ£è¡çºããä¿è·ãã顧客ã«ä¿¡é Œã§ãããµãŒãã¹ãæäŸããããã«ã¬ãŒãå¶éã䜿çšããŠããŸãã
- Eã³ããŒã¹ãã©ãããã©ãŒã ïŒå€ãã®eã³ããŒã¹ãã©ãããã©ãŒã ã¯ãè£œåæ å ±ãã¹ã¯ã¬ã€ãã³ã°ãããããã©ãã·ã¥ã»ãŒã«äžã«ãµãŒãã¹æåŠæ»æãå®è¡ããããããããæ»æããä¿è·ããããã«ã¬ãŒãå¶éã䜿çšããŠããŸãã
- éèæ©é¢ïŒéèæ©é¢ã¯ãæ©å¯æ§ã®é«ã財åããŒã¿ãžã®äžæ£ã¢ã¯ã»ã¹ãé²ããèŠå¶èŠä»¶ãžã®æºæ ã確ä¿ããããã«ãAPIã«ã¬ãŒãå¶éãå®è£ ããŠããŸãã
çµè«
ã¬ãŒãå¶éã¯ãAPIãä¿è·ããã¢ããªã±ãŒã·ã§ã³ã®å®å®æ§ãšä¿¡é Œæ§ã確ä¿ããããã®éèŠãªæè¡ã§ããããŒã¯ã³ãã±ããã¢ã«ãŽãªãºã ãšã¹ã©ã€ãã£ã³ã°ãŠã£ã³ããŠã¢ã«ãŽãªãºã ã¯2ã€ã®äžè¬çãªãªãã·ã§ã³ã§ãããããããã«ç¬èªã®é·æãšçæããããŸãããããã®ã¢ã«ãŽãªãºã ãçè§£ãããã¹ããã©ã¯ãã£ã¹ã«åŸãããšã§ãPythonã¢ããªã±ãŒã·ã§ã³ã§ã¬ãŒãå¶éã广çã«å®è£ ããããå埩åã®ããå®å šãªã·ã¹ãã ãæ§ç¯ã§ããŸããç¹å®ã®èŠä»¶ãèæ ®ããé©åãªã¢ã«ãŽãªãºã ãæ éã«éžæããå®è£ ãç£èŠããŠãããŒãºãæºãããŠããããšã確èªããããšãå¿ããªãã§ãã ãããã¢ããªã±ãŒã·ã§ã³ãã¹ã±ãŒãªã³ã°ããå Žåã¯ããã¹ãŠã®ãµãŒããŒã§äžè²«ããã¬ãŒãå¶éãç¶æããããã«ã忣ã¬ãŒãå¶éæè¡ãæ¡çšããããšãæ€èšããŠãã ãããã¬ãŒãå¶éããããŒãšæçãªãšã©ãŒã¡ãã»ãŒãžãä»ããŠãAPIã³ã³ã·ã¥ãŒããŒãšã®æç¢ºãªã³ãã¥ãã±ãŒã·ã§ã³ã®éèŠæ§ãå¿ããªãã§ãã ããã